package com.emeraldparser.util;

import java.util.Collection;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.ForwardingCollection;

public abstract class InvertedGraph<V, E> extends ForwardingGraph<V, E> {

	private Collection<Edge<V, E>> invertEdges(Collection<Edge<V, E>> edges) {
		final Collection<Edge<V, E>> transformed = Collections2.transform(
				edges,
				new Function<Edge<V, E>, Edge<V, E>>() {
					@Override
					public Edge<V, E> apply(Edge<V, E> edge) {
						return edge.invert();
					}
				}
			);
		
		return new ForwardingCollection<Edge<V,E>>() {

			@Override
			protected Collection<Edge<V, E>> delegate() {
				return transformed;
			}

			@Override
			public boolean add(Edge<V, E> edge) {
				return super.add(edge.invert());
			}

			@Override
			public boolean addAll(Collection<? extends Edge<V, E>> collection) {
				return standardAddAll(collection);
			}
			
		};
	}

	@Override
	public Collection<V> vertices() {
		return super.vertices();
	}

	@Override
	public Collection<E> edgeLabels() {
		return super.edgeLabels();
	}

	@Override
	public Collection<Edge<V, E>> edges() {
		return invertEdges(super.edges());
	}

	@Override
	public Collection<Edge<V, E>> edges(V vertex) {
		return invertEdges(super.edges(vertex));
	}

	@Override
	public Collection<Edge<V, E>> edges(V source, V destination) {
		return invertEdges(super.edges(destination, source));
	}

	@Override
	public Collection<Edge<V, E>> inboundEdges(V vertex) {
		return invertEdges(super.outboundEdges(vertex));
	}

	@Override
	public Collection<Edge<V, E>> outboundEdges(V vertex) {
		return invertEdges(super.inboundEdges(vertex));
	}

	@Override
	public Collection<V> predecessors(V vertex) {
		return super.successors(vertex);
	}

	@Override
	public Collection<V> successors(V vertex) {
		return super.predecessors(vertex);
	}

	@Override
	public Collection<V> predecessors(V vertex, E edgeLabel) {
		return super.successors(vertex, edgeLabel);
	}

	@Override
	public Collection<V> successors(V vertex, E edgeLabel) {
		return super.predecessors(vertex, edgeLabel);
	}

	@Override
	public boolean addDirectedEdge(V source, V destination, E edgeLabel) {
		return super.addDirectedEdge(destination, source, edgeLabel);
	}

	@Override
	public boolean addUndirectedEdge(V source, V destination, E edgeLabel) {
		return super.addUndirectedEdge(destination, source, edgeLabel);
	}

	@Override
	public boolean addVertex(V vertex) {
		return super.addVertex(vertex);
	}

	@Override
	public boolean containsVertex(Object o) {
		return super.containsVertex(o);
	}

	@Override
	public boolean containsEdge(Edge<?, ?> edge) {
		return super.containsEdge(edge.invert());
	}

	@Override
	public boolean addAll(final Graph<? extends V, ? extends E> graph) {
		// TODO Auto-generated method stub
		return super.addAll(
				new InvertedGraph<V, E>() {
					@Override
					public Graph<V, E> delegate() {
						return (Graph) graph;
					}
				}
			);
	}

	@Override
	public Collection<E> inboundEdgeLabels(V vertex) {
		return super.outboundEdgeLabels(vertex);
	}

	@Override
	public Collection<E> outboundEdgeLabels(V vertex) {
		return super.inboundEdgeLabels(vertex);
	}

	@Override
	public Collection<Edge<V, E>> outboundEdges(V vertex, E edgeLabel) {
		return invertEdges(super.inboundEdges(vertex, edgeLabel));
	}

	@Override
	public Collection<Edge<V, E>> inboundEdges(V vertex, E edgeLabel) {
		return invertEdges(super.outboundEdges(vertex, edgeLabel));
	}

	@Override
	public boolean addEdge(Edge<V, E> edge) {
		return super.addEdge(edge.invert());
	}

	@Override
	public String toString() {
		return "Graph[vertices=" + vertices() + ", edges=" + edges() + "]";
	}

}
